package spring.framework.beans.factory.support;

import cn.hutool.core.bean.BeanUtil;
import spring.framework.beans.BeansException;
import spring.framework.beans.PropertyValue;
import spring.framework.beans.PropertyValues;
import spring.framework.beans.factory.config.BeanDefinition;
import spring.framework.beans.factory.config.BeanReference;

import java.lang.reflect.Constructor;

/**
 * @author fun.pengzh
 * @class spring.framework.beans.factory.support.AbstractAutoWireCapableBeanFactory
 * @desc
 * @since 2021-09-22
 */
public abstract class AbstractAutoWireCapableBeanFactory extends AbstractBeanFactory {

    public InstantiationStrategy getInstantiationStrategy() {
        return instantiationStrategy;
    }


    @Override
    Object createBean(BeanDefinition beanDefinition, String beanName, Object[] args) throws BeansException {
        Object bean = null;
        try {
            bean = createInstance(beanDefinition, beanName, args);
            applyPropertyValues(beanDefinition, beanName, bean);
        } catch (Exception e) {
            throw new BeansException("Create Bean Exception :", e);
        }
        addSingleton(beanName, bean);
        return bean;
    }

    Object createInstance(BeanDefinition beanDefinition, String beanName, Object[] args) throws Exception {
        Class beanClass = beanDefinition.getBeanClass();
        Constructor[] constructors = beanClass.getConstructors();
        Constructor constructorToUse = null;
        for (Constructor constructor : constructors) {
            if (args != null && constructor.getParameterTypes().length == args.length) {
                constructorToUse = constructor;
                break;
            }
        }
        return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
    }

    private void applyPropertyValues(BeanDefinition beanDefinition, String beanName, Object bean) throws BeansException {
        try {

            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                String name = propertyValue.getName();
                Object value = propertyValue.getValue();
                if (value instanceof BeanReference) {
                    //A依赖B  获取B的实例 （不处理循环依赖）
                    BeanReference beanReference = (BeanReference) value;
                    value = getBean(beanReference.getBeanName());
                }
                BeanUtil.setFieldValue(bean, name, value);
            }

        } catch (Exception e) {
            throw new BeansException("Error setting of propter values:" + beanName, e);
        }
    }


    public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
        this.instantiationStrategy = instantiationStrategy;
    }

    private InstantiationStrategy instantiationStrategy = new CglibInstantiationStrategy();

}
